package MultiSnake;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.net.Socket;
import java.rmi.RemoteException;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Random;

import javax.swing.JOptionPane;

class MultiSnakeModel implements Runnable,Serializable {
	MultiSnake ms;
	boolean[][] isSnake;
	boolean[][] isFood;
	/*
	 * my snake position and score
	 */
	LinkedList myNodeArray = new LinkedList();
	int myScore = 0;
	String mySpeed = "slow speed";
	/*
	 * opp snake
	 */
	LinkedList oppNodeArray = new LinkedList();
	int oppScore = 0;

	LinkedList[] list;

	Node food;
	static int maxX;
	static int maxY;

	/*
	 * running status
	 */
	public boolean myRunning = false;
	boolean oppRunning = true;

	/*
	 * initial direction
	 */
	int direction = 3;

	/*
	 * 
	 */
	int finalTime = 100;
	int timeInterval = finalTime;
	int slow = 100;
	int med = 70;
	int fast = 50;

	/*
	 * speed
	 */
	double speedChangeRate = 0.5;

	/*
	 * Pause status
	 */
	boolean paused = false;

	int countMove = 0;

	public static final int UP = 2;
	public static final int DOWN = 4;
	public static final int LEFT = 1;
	public static final int RIGHT = 3;

	/*
	 * Stream
	 */
	Socket s;
	//ObjectOutputStream oos;
	//ObjectInputStream ois;

	public MultiSnakeModel(ServerInterface server, MultiSnake ms, int XX, int YY) {
		this.ms = ms;
		this.maxX = Server.Width / Server.nodeWidth;
		this.maxY = Server.Height / Server.nodeHeight;
		//this.s = s;
		/*
		 * initial matirx
		 */
		isSnake = new boolean[maxX][];
		for (int i = 0; i < maxX; ++i) {
			isSnake[i] = new boolean[maxY];
			Arrays.fill(isSnake[i], false);
		}
		isFood = new boolean[maxX][];
		for (int i = 0; i < maxX; ++i) {
			isFood[i] = new boolean[maxY];
			Arrays.fill(isFood[i], false);
		}

		/*
		 * maxX is canvasWidth / nodeWidth
		 */

		int initArrayLength = 2;
		for (int i = 0; i < initArrayLength; ++i) {
			int x = XX;
			int y = YY;
			myNodeArray.addLast(new Node(x, y));
			isSnake[x][y] = true;
		}
		try {
			//oos = new ObjectOutputStream(s.getOutputStream());
			//oos.writeObject("start");
			//server
			//ois = new ObjectInputStream(s.getInputStream());
//			int x = Integer.parseInt(ois.readObject().toString());
//			int y = Integer.parseInt(ois.readObject().toString());
//			isFood = (boolean[][]) ois.readObject();
			//food = new Node(x, y);
			//createFood();
//			server.getFood();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/*
	 * change direction
	 */
	public void changeDirection(int newDirection) {
		if (direction % 2 != newDirection % 2) {
			direction = newDirection;
		}
	}

	public boolean move() {
		Node n = (Node) myNodeArray.getFirst();
		int x = n.x;
		int y = n.y;

		switch (direction) {
		case UP:
			y--;
			break;
		case DOWN:
			y++;
			break;
		case LEFT:
			x--;
			break;
		case RIGHT:
			x++;
			break;
		}

		/*
		 * whether snake run in the board
		 */
		if ((0 <= x && x < maxX) && (0 <= y && y < maxY)) {
			for (int i = 0; i < myNodeArray.size(); i++) {
				for (int j = 0; j < oppNodeArray.size(); j++) {
					Node mine = (Node) myNodeArray.get(i);
					Node oppo = (Node) oppNodeArray.get(j);
					if (i == 0 && j != 0 && mine.x == oppo.x
							&& mine.y == oppo.y) {
//						try {
							myRunning = false;
							oppRunning = false;
//							oos = new ObjectOutputStream(s.getOutputStream());
//							oos.writeObject("stop");
							return false;
//						} catch (IOException e) {
							// TODO Auto-generated catch block
//							e.printStackTrace();
//						}
					} else if (j == 0 && i != 0 && mine.x == oppo.x
							&& mine.y == oppo.y) {
//						try {
							myRunning = false;
							oppRunning = false;
//							oos = new ObjectOutputStream(s.getOutputStream());
//							oos.writeObject("stop");
							return false;
//						} catch (IOException e) {
							// TODO Auto-generated catch block
//							e.printStackTrace();
//						}
					} else if (j == 0 && i == 0 && mine.x == oppo.x
							&& mine.y == oppo.y) {
//						try {
							myRunning = false;
							oppRunning = false;
//							oos = new ObjectOutputStream(s.getOutputStream());
//							oos.writeObject("stop");
							return false;
//						} catch (IOException e) {
							// TODO Auto-generated catch block
//							e.printStackTrace();
//						}
						// JOptionPane.showMessageDialog(null,
						// "You are both losers!", "Game Over",
						// JOptionPane.INFORMATION_MESSAGE);
						// System.exit(-1);
					}
				}
			}
			if (isSnake[x][y]) {
				return false;
			} else if (isFood[x][y]) {
				/*
				 * whether it is food
				 */
				if (x == food.x && y == food.y) {
					myNodeArray.addFirst(food);
					// get score
					// int scoreGet = (10000 - 200 * countMove) / timeInterval;
					// myScore += scoreGet > 0 ? scoreGet : 10;
					myScore += 10;
					countMove = 0;
					food = createFood();
					return true;
				}
			} else {
				myNodeArray.addFirst(new Node(x, y));
				isSnake[x][y] = true;
				n = (Node) myNodeArray.removeLast();
				isSnake[n.x][n.y] = false;
				countMove++;
				return true;
			}
		}
		if (x >= maxX) {
			myNodeArray.addFirst(new Node(0, y));
			isSnake[0][y] = true;
			n = (Node) myNodeArray.removeLast();
			isSnake[n.x][n.y] = false;
			return true;
		}
		if (x < 0) {
			myNodeArray.addFirst(new Node(maxX - 2, y));
			isSnake[maxX - 2][y] = true;
			n = (Node) myNodeArray.removeLast();
			isSnake[n.x][n.y] = false;
			return true;
		}
		if (y >= maxY) {
			myNodeArray.addFirst(new Node(x, 0));
			isSnake[x][0] = true;
			n = (Node) myNodeArray.removeLast();
			isSnake[n.x][n.y] = false;
			return true;
		}
		if (y < 0) {
			myNodeArray.addFirst(new Node(x, maxY - 2));
			isSnake[x][maxY - 2] = true;
			n = (Node) myNodeArray.removeLast();
			isSnake[n.x][n.y] = false;
			return true;
		}
		return false;
	}

	public void getAllMsg() {
		try {
//			oos = new ObjectOutputStream(s.getOutputStream());
//			oos.writeObject("update");
//			oos.writeObject(myNodeArray);
//			oos.writeObject(myRunning);

//			ois = new ObjectInputStream(s.getInputStream());
//			oppNodeArray = (LinkedList) ois.readObject();
//			oppScore = Integer.parseInt(ois.readObject().toString());
//			oppRunning = (Boolean) ois.readObject();
//			int x = Integer.parseInt(ois.readObject().toString());
//			int y = Integer.parseInt(ois.readObject().toString());
//			food = new Node(x, y);
//			isFood = (boolean[][]) ois.readObject();

		} catch (Exception e) {
		}
	}

	public void run() {
		System.out.println("in model run");
		System.out.println("oppRunning="+oppRunning);
		myRunning = true;
		while (myRunning && oppRunning) {
			try {
				Thread.sleep(timeInterval);
			} catch (Exception e) {
				break;
			}
			if (move()) {
				getAllMsg();
				ms.repaint();
			} else {
				break;
			}
		}
		myRunning = false;
//		try {
//			oos = new ObjectOutputStream(s.getOutputStream());
//			oos.writeObject("stop");
//			oos.close();
//		} catch (IOException e) {
//			e.printStackTrace();
//		}

		if (myScore > oppScore) {
			JOptionPane.showMessageDialog(null, "You are winner!", "Game Over",
					JOptionPane.INFORMATION_MESSAGE);
			System.exit(-1);
		} else if (myScore < oppScore) {
			JOptionPane.showMessageDialog(null, "You are loser!", "Game Over",
					JOptionPane.INFORMATION_MESSAGE);
			System.exit(-1);
		} else {
			JOptionPane.showMessageDialog(null, "You are both winner!",
					"Game Over", JOptionPane.INFORMATION_MESSAGE);
			System.exit(-1);
		}
	}

	/*
	 * Random a food
	 */
	public Node createFood() {

		int x = 0;
		int y = 0;
		try {
//			oos = new ObjectOutputStream(s.getOutputStream());
//			oos.writeObject("food");
//			oos.writeObject(myScore + "");
//			ois = new ObjectInputStream(s.getInputStream());
//			x = Integer.parseInt(ois.readObject().toString());
//			y = Integer.parseInt(ois.readObject().toString());
//			isFood = (boolean[][]) ois.readObject();
			Random r = new Random();
			x = r.nextInt(maxX);
			y = r.nextInt(maxY);
			isFood[x][y] = true;
//			food = new Node(x, y);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return new Node(x, y);
	}

	/*
	 * increase speed
	 */
	public void speedUp() {
		try {
			if (timeInterval == slow) {
				timeInterval = med;
				mySpeed = "Med speed";
			} else if (timeInterval == med) {
				timeInterval = fast;
				mySpeed = "fast speed";
			}
			ms.updateSpeed();
		} catch (Exception e) {
		}

	}

	/*
	 * decrease speed
	 */
	public void speedDown() {
		try {
			if (timeInterval == fast) {
				timeInterval = med;
				mySpeed = "Med speed";
			} else if (timeInterval == med) {
				timeInterval = slow;
				mySpeed = "slow speed";
			}
			ms.updateSpeed();
		} catch (Exception e) {
		}
	}
}

class Node implements Serializable {
	int x;
	int y;

	Node(int x, int y) {
		this.x = x;
		this.y = y;
	}
}